﻿using System;
using System.Runtime.InteropServices;
using System.Drawing.Printing;
using System.ComponentModel;
using System.Text;
using System.Windows.Forms;

namespace iPdf
{
    #region "Data structure"

    #region PRINTER_DEFAULTS
    [StructLayout(LayoutKind.Sequential)]
    public struct PRINTER_DEFAULTS
    {
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "Datatype")]
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields")]
        public int pDatatype;
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields")]
        public int pDevMode;
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields")]
        public int DesiredAccess;
    }
    #endregion PRINTER_DEFAULTS

    #region PRINTER_INFO_2
    [StructLayout(LayoutKind.Sequential)]
    struct PRINTER_INFO_2
    {
        [MarshalAs(UnmanagedType.LPStr)]
        public string pServerName;
        [MarshalAs(UnmanagedType.LPStr)]
        public string pPrinterName;
        [MarshalAs(UnmanagedType.LPStr)]
        public string pShareName;
        [MarshalAs(UnmanagedType.LPStr)]
        public string pPortName;
        [MarshalAs(UnmanagedType.LPStr)]
        public string pDriverName;
        [MarshalAs(UnmanagedType.LPStr)]
        public string pComment;
        [MarshalAs(UnmanagedType.LPStr)]
        public string pLocation;
        public IntPtr pDevMode;
        [MarshalAs(UnmanagedType.LPStr)]
        public string pSepFile;
        [MarshalAs(UnmanagedType.LPStr)]
        public string pPrintProcessor;
        [MarshalAs(UnmanagedType.LPStr)]
        public string pDatatype;
        [MarshalAs(UnmanagedType.LPStr)]
        public string pParameters;
        public IntPtr pSecurityDescriptor;
        public Int32 Attributes;
        public Int32 Priority;
        public Int32 DefaultPriority;
        public Int32 StartTime;
        public Int32 UntilTime;
        public Int32 Status;
        public Int32 cJobs;
        public Int32 AveragePPM;
    }
    #endregion PRINTER_INFO_2

    #region DEVMODE
    [StructLayout(LayoutKind.Sequential)]
    public struct DEVMODE
    {
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields")]
        public string dmDeviceName;
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields")]
        public short dmSpecVersion;
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields")]
        public short dmDriverVersion;
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields")]
        public short dmSize;
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields")]
        public short dmDriverExtra;
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields")]
        public int dmFields;
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields")]
        public short dmOrientation;
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields")]
        public short dmPaperSize;
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields")]
        public short dmPaperLength;
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields")]
        public short dmPaperWidth;
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields")]
        public short dmScale;
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields")]
        public short dmCopies;
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields")]
        public short dmDefaultSource;
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields")]
        public short dmPrintQuality;
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields")]
        public short dmColor;
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields")]
        public short dmDuplex;
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields")]
        public short dmYResolution;
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields")]
        public short dmTTOption;
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields")]
        public short dmCollate;

        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields")]
        public string dmFormName;
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields")]
        public short dmUnusedPadding;
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields")]
        public short dmBitsPerPel;
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields")]
        public int dmPelsWidth;
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields")]
        public int dmPelsHeight;
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields")]
        public int dmDisplayFlags;
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields")]
        public int dmDisplayFrequency;
    }
    #endregion DEVMODE

    #endregion	"Data structure"

    #region PrinterConfigurator
    public class PrinterConfigurator
    {
        private static PaperSize a4Paper = new PaperSize("A4", UnitConverter.PointsToHundredthInches(595),
                                                UnitConverter.PointsToHundredthInches(842) );
        private static PaperSize letterPaper = new PaperSize("Letter", UnitConverter.PointsToHundredthInches(612),
                                        UnitConverter.PointsToHundredthInches(792));
        #region Properties
        public static PaperSize A4Paper
        {
            get
            {
                return a4Paper;
            }
        }

        public static PaperSize LetterPaper
        {
            get
            {
                return letterPaper;
            }
        }
        #endregion

        #region "Private Variables"
        private static IntPtr _hPrinter = new System.IntPtr();
        private static PRINTER_DEFAULTS _PrinterValues = new PRINTER_DEFAULTS();
        private static PRINTER_INFO_2 _pinfo = new PRINTER_INFO_2();
        private static DEVMODE _Devmode;
        private static IntPtr _PtrDM;
        private static IntPtr _PtrPrinterInfo;
        private static int _SizeOfDevMode = 0;
        private static int _LastError;
        private static int _NBytesNeeded;
        private static long _NRet;
        private static int _IntError;
        private static System.Int32 _NJunk;
        private static IntPtr _YDevModeData;
        #endregion

        #region "Win API Def"

        [DllImport("kernel32.dll", EntryPoint = "GetLastError", SetLastError = false,
        ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass")]
        private static extern Int32 GetLastError();

        [DllImport("winspool.Drv", EntryPoint = "ClosePrinter", SetLastError = true,
        ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1414:MarkBooleanPInvokeArgumentsWithMarshalAs")]
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass")]
        private static extern  bool ClosePrinter(IntPtr hPrinter);

        [DllImport("winspool.Drv", EntryPoint = "DocumentPropertiesA", SetLastError = true,
        ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA2101:SpecifyMarshalingForPInvokeStringArguments", MessageId = "2")]
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass")]
        private static extern int DocumentProperties(IntPtr hwnd, IntPtr hPrinter,
        [MarshalAs(UnmanagedType.LPStr)] string pDeviceNameg,
        IntPtr pDevModeOutput, ref IntPtr pDevModeInput, int fMode);

        [DllImport("winspool.Drv", EntryPoint = "GetPrinterA", SetLastError = true, CharSet = CharSet.Ansi,
        ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1414:MarkBooleanPInvokeArgumentsWithMarshalAs")]
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass")]
        private static extern bool GetPrinter(IntPtr hPrinter, Int32 dwLevel,
        IntPtr pPrinter, Int32 dwBuf, out Int32 dwNeeded);

        [DllImport("winspool.Drv", EntryPoint = "OpenPrinterA", SetLastError = true, CharSet = CharSet.Ansi,
        ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA2101:SpecifyMarshalingForPInvokeStringArguments", MessageId = "0")]
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1414:MarkBooleanPInvokeArgumentsWithMarshalAs")]
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass")]
        private static extern bool OpenPrinter([MarshalAs(UnmanagedType.LPStr)] string szPrinter,
        out IntPtr hPrinter, ref PRINTER_DEFAULTS pd);

        [DllImport("winspool.drv", CharSet = CharSet.Ansi, SetLastError = true)]
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1414:MarkBooleanPInvokeArgumentsWithMarshalAs")]
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass")]
        private static extern bool SetPrinter(IntPtr hPrinter, int Level, IntPtr
        pPrinter, int Command);

        [DllImport("Winspool.drv", CharSet=CharSet.Auto, SetLastError=true)]
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA2101:SpecifyMarshalingForPInvokeStringArguments", MessageId = "0")]
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1414:MarkBooleanPInvokeArgumentsWithMarshalAs")]
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass")]
        private static extern bool SetDefaultPrinter(string printerName); 

        [DllImport("winspool.drv", CharSet=CharSet.Auto, SetLastError=true)]
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA2101:SpecifyMarshalingForPInvokeStringArguments", MessageId = "0")]
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1414:MarkBooleanPInvokeArgumentsWithMarshalAs")]
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass")]
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1401:PInvokesShouldNotBeVisible")]
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference", MessageId = "1#")]
        public static extern bool GetDefaultPrinter(StringBuilder pszBuffer, ref int size);

        [DllImport("kernel32.dll")]
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass")]
        static extern IntPtr GlobalLock(IntPtr hMem);
        [DllImport("kernel32.dll")]
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1414:MarkBooleanPInvokeArgumentsWithMarshalAs")]
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass")]
        static extern bool GlobalUnlock(IntPtr hMem);
        [DllImport("kernel32.dll")]
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1414:MarkBooleanPInvokeArgumentsWithMarshalAs")]
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass")]
        static extern IntPtr GlobalFree(IntPtr hMem);
        #endregion

        #region "Constants"
        private const int DM_DUPLEX = 0x1000;
        private const int DM_IN_BUFFER = 8;
        private const int DM_OUT_BUFFER = 2;
        private const int PRINTER_ACCESS_ADMINISTER = 0x4;
        private const int PRINTER_ACCESS_USE = 0x8;
        private const int STANDARD_RIGHTS_REQUIRED = 0xF0000;
        private const int PRINTER_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED | PRINTER_ACCESS_ADMINISTER | PRINTER_ACCESS_USE);
        #endregion

        #region "Function to change printer settings"

        #region ChangePrinterPaperSize(string PrinterName, PaperSize papersize)
        public static bool ChangePrinterPaperSize(string printerName, PaperSize papersize)
        {
            if (string.IsNullOrEmpty(printerName))
            {
                printerName = GetDefaultPrinter();
            }

            if (papersize == null)
            {
                papersize = A4Paper;
            }

            PaperSize defaultPapersize = GetDefaultPapersize(printerName);
            if (defaultPapersize.RawKind == papersize.RawKind)
            {
                return true;
            }

            try
            {
                _Devmode = GetPrinterSettings(printerName);
                _Devmode.dmPaperSize = (short)papersize.Kind;

                if (papersize.Kind == PaperKind.Custom)
                {
                    _Devmode.dmPaperWidth =
                    Convert.ToInt16((double)Math.Round(((Convert.ToDouble(papersize.Width)) / 100D) * 25.4D * 10D));
                    _Devmode.dmPaperLength =
                    Convert.ToInt16((double)Math.Round((Convert.ToDouble(papersize.Height) / 100D) * 25.4D * 10D));
                }

                // set the form name field to indicate that this it will be modified
                _Devmode.dmFields = 0x10000;
                // set the form name
                _Devmode.dmFormName = papersize.PaperName;
                Marshal.StructureToPtr(_Devmode, _YDevModeData, true);
                _pinfo.pDevMode = _YDevModeData;
                _pinfo.pSecurityDescriptor = IntPtr.Zero;
                Marshal.StructureToPtr(_pinfo, _PtrPrinterInfo, true);
                _LastError = Marshal.GetLastWin32Error();
                _NRet = Convert.ToInt16(SetPrinter(_hPrinter, 2, _PtrPrinterInfo, 0));

                if (_NRet == 0)
                {
                    //Unable to set shared printer settings.
                    _LastError = Marshal.GetLastWin32Error();
                    throw new Win32Exception(Marshal.GetLastWin32Error());
                }
            }
            finally
            {
                if (_hPrinter != IntPtr.Zero)
                {
                    ClosePrinter(_hPrinter);
                }
            }

            return Convert.ToBoolean(_NRet);
        }

        #endregion (string PrinterName, PaperSize papersize)

        #region ChangePrinterOrientation(string PrinterName,bool landscape)
        public static bool ChangePrinterOrientation(string printerName, bool landscape)
        {
            try
            {
                _Devmode = GetPrinterSettings(printerName);
                _Devmode.dmOrientation = (short)((landscape) ? 2 : 1);
                Marshal.StructureToPtr(_Devmode, _YDevModeData, true);
                _pinfo.pDevMode = _YDevModeData;
                _pinfo.pSecurityDescriptor = IntPtr.Zero;
                Marshal.StructureToPtr(_pinfo, _PtrPrinterInfo, true);
                _LastError = Marshal.GetLastWin32Error();
                _NRet = Convert.ToInt16(SetPrinter(_hPrinter, 2, _PtrPrinterInfo, 0));

                if (_NRet == 0)
                {
                    //Unable to set shared printer settings.

                    _LastError = Marshal.GetLastWin32Error();
                    //string myErrMsg = GetErrorMessage(_LastError);
                    throw new Win32Exception(Marshal.GetLastWin32Error());
                }
            }
            finally
            {
                if (_hPrinter != IntPtr.Zero)
                {

                    ClosePrinter(_hPrinter);
                }
            }

            return Convert.ToBoolean(_NRet);
        }
        #endregion ChangePrinterOrientation(string PrinterName,bool landscape)

        #region ChangePrinterSetting(string PrinterName,PageSettings pageSettings)
        public static bool ChangePrinterSetting(string PrinterName, PageSettings pageSettings)
        {
            try
            {
                _Devmode = GetPrinterSettings(PrinterName);
                _Devmode.dmDefaultSource = (short)pageSettings.PaperSource.Kind;
                _Devmode.dmOrientation = (short)((pageSettings.Landscape) ? 2 : 1);
                _Devmode.dmPaperSize = (short)pageSettings.PaperSize.Kind;
                if (pageSettings.PaperSize.Kind == PaperKind.Custom)
                {
                    _Devmode.dmPaperWidth =
                    Convert.ToInt16((double)Math.Round(((Convert.ToDouble(pageSettings.PaperSize.Width)) / 100D) * 25.4D * 10D));
                    _Devmode.dmPaperLength =
                    Convert.ToInt16((double)Math.Round((Convert.ToDouble(pageSettings.PaperSize.Height) / 100D) * 25.4D * 10D));
                }
                Marshal.StructureToPtr(_Devmode, _YDevModeData, true);
                _pinfo.pDevMode = _YDevModeData;
                _pinfo.pSecurityDescriptor = IntPtr.Zero;
                /*update driver dependent part of the DEVMODE
                1 = DocumentProperties(IntPtr.Zero, _hPrinter, sPrinterName, _YDevModeData
                , ref _pinfo.pDevMode, (DM_IN_BUFFER | DM_OUT_BUFFER));*/
                Marshal.StructureToPtr(_pinfo, _PtrPrinterInfo, true);
                _LastError = Marshal.GetLastWin32Error();
                _NRet = Convert.ToInt16(SetPrinter(_hPrinter, 2, _PtrPrinterInfo, 0));

                if (_NRet == 0)
                {
                    //Unable to set shared printer settings.
                    _LastError = Marshal.GetLastWin32Error();
                    //string myErrMsg = GetErrorMessage(_LastError);
                    throw new Win32Exception(Marshal.GetLastWin32Error());
                }
            }
            finally
            {
                if (_hPrinter != IntPtr.Zero)
                {

                    ClosePrinter(_hPrinter);
                }
            }
            return Convert.ToBoolean(_NRet);
        }
        #endregion ChangePrinterSetting(string PrinterName,PageSettings pageSettings)

        #region ChangePrinterSetting(string PrinterName,PageSettings pageSettings, Duplex duplex)
        public static bool ChangePrinterSetting(string PrinterName, PageSettings pageSettings, Duplex duplex)
        {
            try
            {
                _Devmode = GetPrinterSettings(PrinterName);
                _Devmode.dmDefaultSource = (short)pageSettings.PaperSource.Kind;
                _Devmode.dmOrientation = (short)((pageSettings.Landscape) ? 2 : 1);
                _Devmode.dmPaperSize = (short)pageSettings.PaperSize.Kind;
                if (pageSettings.PaperSize.Kind == PaperKind.Custom)
                {
                    _Devmode.dmPaperWidth =
                    Convert.ToInt16((double)Math.Round(((Convert.ToDouble(pageSettings.PaperSize.Width)) / 100D) * 25.4D * 10D));
                    _Devmode.dmPaperLength =
                    Convert.ToInt16((double)Math.Round((Convert.ToDouble(pageSettings.PaperSize.Height) / 100D) * 25.4D * 10D));
                }
                _Devmode.dmDuplex = (short)duplex;
                Marshal.StructureToPtr(_Devmode, _YDevModeData, true);
                _pinfo.pDevMode = _YDevModeData;
                _pinfo.pSecurityDescriptor = IntPtr.Zero;
                /*update driver dependent part of the DEVMODE
                1 = DocumentProperties(IntPtr.Zero, _hPrinter, sPrinterName, _YDevModeData
                , ref _pinfo.pDevMode, (DM_IN_BUFFER | DM_OUT_BUFFER));*/
                Marshal.StructureToPtr(_pinfo, _PtrPrinterInfo, true);
                _LastError = Marshal.GetLastWin32Error();
                _NRet = Convert.ToInt16(SetPrinter(_hPrinter, 2, _PtrPrinterInfo, 0));

                if (_NRet == 0)
                {
                    //Unable to set shared printer settings.

                    _LastError = Marshal.GetLastWin32Error();
                    //string myErrMsg = GetErrorMessage(_LastError);
                    throw new Win32Exception(Marshal.GetLastWin32Error());
                }
            }
            finally
            {
                if (_hPrinter != IntPtr.Zero)
                {

                    ClosePrinter(_hPrinter);
                }
            }
            return Convert.ToBoolean(_NRet);
        }
        #endregion ChangePrinterSetting(string PrinterName,PageSettings pageSettings, bool landscape)

        #region ChangePrinterSetting(string PrinterName,Duplex duplex)
        public static bool ChangePrinterSetting(string PrinterName, Duplex duplex)
        {
            try
            {
                _Devmode = GetPrinterSettings(PrinterName);
                _Devmode.dmDuplex = (short)duplex;
                Marshal.StructureToPtr(_Devmode, _YDevModeData, true);
                _pinfo.pDevMode = _YDevModeData;
                _pinfo.pSecurityDescriptor = IntPtr.Zero;
                /*update driver dependent part of the DEVMODE
                1 = DocumentProperties(IntPtr.Zero, _hPrinter, sPrinterName, _YDevModeData
                , ref _pinfo.pDevMode, (DM_IN_BUFFER | DM_OUT_BUFFER));*/
                Marshal.StructureToPtr(_pinfo, _PtrPrinterInfo, true);
                _LastError = Marshal.GetLastWin32Error();
                _NRet = Convert.ToInt16(SetPrinter(_hPrinter, 2, _PtrPrinterInfo, 0));

                if (_NRet == 0)
                {
                    //Unable to set shared printer settings.

                    _LastError = Marshal.GetLastWin32Error();
                    //string myErrMsg = GetErrorMessage(_LastError);
                    throw new Win32Exception(Marshal.GetLastWin32Error());
                }
            }
            finally
            {
                if (_hPrinter != IntPtr.Zero)
                {

                    ClosePrinter(_hPrinter);
                }
            }
            return Convert.ToBoolean(_NRet);
        }

        #endregion ChangePrinterSetting(string PrinterName,Duplex duplex)

        #region GetPrinterSettings(string PrinterName)
        private static DEVMODE GetPrinterSettings(string PrinterName)
        {
            DEVMODE devmode;
            const int PRINTER_ACCESS_ADMINISTER = 0x4;
            const int PRINTER_ACCESS_USE = 0x8;
            const int PRINTER_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED | PRINTER_ACCESS_ADMINISTER | PRINTER_ACCESS_USE);

            _PrinterValues.pDatatype = 0;
            _PrinterValues.pDevMode = 0;
            _PrinterValues.DesiredAccess = PRINTER_ALL_ACCESS;
            _NRet = Convert.ToInt32(OpenPrinter(PrinterName, out _hPrinter, ref _PrinterValues));
            if (_NRet == 0)
            {
                _LastError = Marshal.GetLastWin32Error();
                throw new Win32Exception(Marshal.GetLastWin32Error());
            }
            GetPrinter(_hPrinter, 2, IntPtr.Zero, 0, out _NBytesNeeded);
            if (_NBytesNeeded <= 0)
            {
                throw new System.Exception("Unable to allocate memory");
            }
            else
            {
                // Allocate enough space for PRINTER_INFO_2... 
                //{ptrPrinterIn fo = Marshal.AllocCoTaskMem(_NBytesNeeded)};
                _PtrPrinterInfo = Marshal.AllocHGlobal(_NBytesNeeded);

                // The second GetPrinter fills in all the current settings, so all you 
                //need to do is modify what you're interested in...
                _NRet = Convert.ToInt32(GetPrinter(_hPrinter, 2, _PtrPrinterInfo, 
                    _NBytesNeeded, out _NJunk));

                if (_NRet == 0)
                {
                    _LastError = Marshal.GetLastWin32Error();
                    throw new Win32Exception(Marshal.GetLastWin32Error());
                }
                _pinfo = (PRINTER_INFO_2)Marshal.PtrToStructure(_PtrPrinterInfo, 
                                typeof(PRINTER_INFO_2));
                IntPtr Temp = new IntPtr();
                if (_pinfo.pDevMode == IntPtr.Zero)
                {
                    // If GetPrinter didn't fill in the DEVMODE, try to get it by calling
                    // DocumentProperties...
                    IntPtr ptrZero = IntPtr.Zero;
                    //get the size of the devmode structure
                    _SizeOfDevMode = DocumentProperties(IntPtr.Zero, _hPrinter, PrinterName, 
                                                ptrZero, ref ptrZero, 0);
                    _PtrDM = Marshal.AllocCoTaskMem(_SizeOfDevMode);
                    int i;
                    i = DocumentProperties(IntPtr.Zero, _hPrinter, PrinterName, _PtrDM, ref ptrZero, 
                                                DM_OUT_BUFFER);
                    if ((i < 0) || (_PtrDM == IntPtr.Zero))
                    {
                        //Cannot get the DEVMODE structure.
                        throw new System.Exception("Cannot get DEVMODE data");
                    }
                    _pinfo.pDevMode = _PtrDM;
                }
                _IntError = DocumentProperties(IntPtr.Zero, _hPrinter, PrinterName, IntPtr.Zero, ref Temp, 0);
                _YDevModeData = Marshal.AllocHGlobal(_IntError);
                _IntError = DocumentProperties(IntPtr.Zero, _hPrinter, PrinterName, _YDevModeData, ref Temp, 2);
                devmode = (DEVMODE)Marshal.PtrToStructure(_YDevModeData, typeof(DEVMODE));
                if ((_NRet == 0) || (_hPrinter == IntPtr.Zero))
                {
                    _LastError = Marshal.GetLastWin32Error();
                    throw new Win32Exception(Marshal.GetLastWin32Error());
                }
                return devmode;
            }

        }
        #endregion GetPrinterSettings(string PrinterName)

        public static void ChangeDefaultPrinter(string printerName)
        {
            if (GetDefaultPrinter() == printerName)
            {
                return;
            }

            SetDefaultPrinter(printerName);

            //sleep a while for the changes to take effects
            Helper.Sleep(Global.TimeToWaitForChangesToTakeEffects);
        }

        public static string GetDefaultPrinter()
        {
            PrintDocument pd = new PrintDocument();
            string defaultPrinterName = pd.PrinterSettings.PrinterName;
            return defaultPrinterName;
        }

        public static PaperSize GetDefaultPapersize(string printerName)
        {
            PrinterSettings prntSettings = new PrinterSettings();
            prntSettings.PrinterName = printerName;
            return prntSettings.DefaultPageSettings.PaperSize;
        }


        public static void OpenPrinterPropertiesDialog(IntPtr parentWindowHandle, PrinterSettings printerSettings)
        {
            IntPtr hDevMode = printerSettings.GetHdevmode(printerSettings.DefaultPageSettings);
            IntPtr pDevMode = GlobalLock(hDevMode);
            int sizeNeeded = DocumentProperties(parentWindowHandle, IntPtr.Zero, printerSettings.PrinterName, pDevMode, ref pDevMode, 0);
            IntPtr devModeData = Marshal.AllocHGlobal(sizeNeeded);
            DocumentProperties(parentWindowHandle, IntPtr.Zero, printerSettings.PrinterName, devModeData, ref pDevMode, 14);
            GlobalUnlock(hDevMode);
            printerSettings.SetHdevmode(devModeData);
            printerSettings.DefaultPageSettings.SetHdevmode(devModeData);
            GlobalFree(hDevMode);
            Marshal.FreeHGlobal(devModeData);
        }

        #endregion	"Function to change printer settings"

        #region "Utilities"

        public static PaperSize SelectPapersizeByKind(string printerName, int kind)
        {
            PrinterSettings prntSettings = new PrinterSettings();
            prntSettings.PrinterName = printerName;
            PaperSize papersize = null;

            foreach (PaperSize ps in prntSettings.PaperSizes)
            {
                if (ps.RawKind == kind ||
                   (int)ps.Kind == kind)
                {
                    papersize = ps;
                    break;
                }
            }

            return papersize;
        }


        #endregion

        #region Add Custom Paper Size to the specified printer

        /// <summary>
        /// Adds the printer form to the default printer
        /// </summary>
        /// <param name="paperName">Name of the printer form</param>
        /// <param name="widthMm">Width given in millimeters</param>
        /// <param name="heightMm">Height given in millimeters</param>
        public static void AddCustomPaperSizeToDefaultPrinter(string paperName, float widthMm, float heightMm)
        {
            AddCustomPaperSize(GetDefaultPrinter(), paperName, widthMm, heightMm);
        }

        /// <summary>
        /// Add the printer form to a printer
        /// </summary>
        /// <param name="printerName">The printer name</param>
        /// <param name="paperName">Name of the printer form</param>
        /// <param name="widthMm">Width given in millimeters</param>
        /// <param name="heightMm">Height given in millimeters</param>
        public static void AddCustomPaperSize(string printerName, string paperName, float
                                              widthMm, float heightMm)
        {
            if (PlatformID.Win32NT == Environment.OSVersion.Platform)
            {
                // The code to add a custom paper size is different for Windows NT than it is
                // for previous versions of windows

                WinApi.structPrinterDefaults defaults = new WinApi.structPrinterDefaults();
                defaults.pDatatype = null;
                defaults.pDevMode = IntPtr.Zero;
                defaults.DesiredAccess = WinApi.PRINTER_ACCESS_ADMINISTER | WinApi.PRINTER_ACCESS_USE;

                IntPtr hPrinter = IntPtr.Zero;

                // Open the printer.
                if (WinApi.OpenPrinter(printerName, out hPrinter, ref defaults))
                {
                    try
                    {
                        // delete the form incase it already exists
                        WinApi.DeleteForm(hPrinter, paperName);

                        // create and initialize the FormInfo structure
                        WinApi.FormInfo formInfo = new WinApi.FormInfo();
                        formInfo.Flags = 0;
                        formInfo.pName = paperName;
                        // all sizes in 1000ths of millimeters
                        formInfo.Size.width = (int)Math.Round(widthMm * 1000.0);
                        formInfo.Size.height = (int)Math.Round(heightMm * 1000.0);
                        formInfo.ImageableArea.left = 0;
                        formInfo.ImageableArea.right = formInfo.Size.width;
                        formInfo.ImageableArea.top = 0;
                        formInfo.ImageableArea.bottom = formInfo.Size.height;
                        if (!WinApi.AddForm(hPrinter, 1, ref formInfo))
                        {
                            StringBuilder strBuilder = new StringBuilder();
                            strBuilder.AppendFormat("Failed to add the custom paper size {0} to the printer {1}, System error number: {2}",
                                                    paperName, printerName, WinApi.GetLastError());
                            throw new ApplicationException(strBuilder.ToString());
                        }

                        // INIT
                        const int DM_OUT_BUFFER = 2;
                        const int DM_IN_BUFFER = 8;
                        WinApi.structDevMode devMode = new WinApi.structDevMode();
                        IntPtr hPrinterInfo, hDummy;
                        WinApi.PRINTER_INFO_9 printerInfo;
                        printerInfo.pDevMode = IntPtr.Zero;
                        int iPrinterInfoSize, iDummyInt;


                        // GET THE SIZE OF THE DEV_MODE BUFFER
                        int iDevModeSize = WinApi.DocumentProperties(IntPtr.Zero, hPrinter, printerName, IntPtr.Zero, IntPtr.Zero, 0);

                        if (iDevModeSize < 0)
                            throw new ApplicationException("Cannot get the size of the DEVMODE structure.");

                        // ALLOCATE THE BUFFER
                        IntPtr hDevMode = Marshal.AllocCoTaskMem(iDevModeSize + 100);

                        // GET A POINTER TO THE DEV_MODE BUFFER
                        int iRet = WinApi.DocumentProperties(IntPtr.Zero, hPrinter, printerName, hDevMode, IntPtr.Zero, DM_OUT_BUFFER);

                        if (iRet < 0)
                            throw new ApplicationException("Cannot get the DEVMODE structure.");

                        // FILL THE DEV_MODE STRUCTURE
                        devMode = (WinApi.structDevMode)Marshal.PtrToStructure(hDevMode, devMode.GetType());

                        // SET THE FORM NAME FIELDS TO INDICATE THAT THIS FIELD WILL BE MODIFIED
                        devMode.dmFields = 0x10000;
                        // SET THE FORM NAME
                        devMode.dmFormName = paperName;

                        // PUT THE DEV_MODE STRUCTURE BACK INTO THE POINTER
                        Marshal.StructureToPtr(devMode, hDevMode, true);

                        // MERGE THE NEW CHAGES WITH THE OLD
                        iRet = WinApi.DocumentProperties(IntPtr.Zero, hPrinter, printerName,
                                                  printerInfo.pDevMode, printerInfo.pDevMode, DM_IN_BUFFER | DM_OUT_BUFFER);

                        if (iRet < 0)
                            throw new ApplicationException("Unable to set the orientation setting for this printer.");

                        // GET THE PRINTER INFO SIZE
                        WinApi.GetPrinter(hPrinter, 9, IntPtr.Zero, 0, out iPrinterInfoSize);
                        if (iPrinterInfoSize == 0)
                            throw new ApplicationException("GetPrinter failed. Couldn't get the # bytes needed for shared PRINTER_INFO_9 structure");

                        // ALLOCATE THE BUFFER
                        hPrinterInfo = Marshal.AllocCoTaskMem(iPrinterInfoSize + 100);

                        // GET A POINTER TO THE PRINTER INFO BUFFER
                        bool bSuccess = WinApi.GetPrinter(hPrinter, 9, hPrinterInfo, iPrinterInfoSize, out iDummyInt);

                        if (!bSuccess)
                            throw new ApplicationException("GetPrinter failed. Couldn't get the shared PRINTER_INFO_9 structure");

                        // FILL THE PRINTER INFO STRUCTURE
                        printerInfo = (WinApi.PRINTER_INFO_9)Marshal.PtrToStructure(hPrinterInfo, printerInfo.GetType());
                        printerInfo.pDevMode = hDevMode;

                        // GET A POINTER TO THE PRINTER INFO STRUCTURE
                        Marshal.StructureToPtr(printerInfo, hPrinterInfo, true);

                        // SET THE PRINTER SETTINGS
                        bSuccess = WinApi.SetPrinter(hPrinter, 9, hPrinterInfo, 0);

                        if (!bSuccess)
                            throw new Win32Exception(Marshal.GetLastWin32Error(), "SetPrinter() failed.  Couldn't set the printer settings");

                        // Tell all open programs that this change occurred.
                        WinApi.SendMessageTimeout(
                            new IntPtr(WinApi.HWND_BROADCAST),
                            WinApi.WM_SETTINGCHANGE,
                            IntPtr.Zero,
                            IntPtr.Zero,
                            WinApi.SendMessageTimeoutFlags.SMTO_NORMAL,
                            1000,
                            out hDummy);
                    }
                    finally
                    {
                        WinApi.ClosePrinter(hPrinter);
                    }
                }
                else
                {
                    StringBuilder strBuilder = new StringBuilder();
                    strBuilder.AppendFormat("Failed to open the {0} printer, System error number: {1}",
                                            printerName, WinApi.GetLastError());
                    throw new ApplicationException(strBuilder.ToString());
                }
            }
            else
            {
                WinApi.structDevMode pDevMode = new WinApi.structDevMode();
                IntPtr hDC = WinApi.CreateDC(null, printerName, null, ref pDevMode);
                if (hDC != IntPtr.Zero)
                {
                    pDevMode.dmFields = (int)(WinApi.DM_PAPERSIZE | WinApi.DM_PAPERWIDTH | WinApi.DM_PAPERLENGTH);
                    pDevMode.dmPaperSize = 256;
                    pDevMode.dmPaperWidth = (short)(widthMm * 1000.0);
                    pDevMode.dmPaperLength = (short)(heightMm * 1000.0);
                    WinApi.ResetDC(hDC, ref pDevMode);
                    WinApi.DeleteDC(hDC);
                }
            }
        }

        #endregion

        #region Delete a specific PaperSize

        public static void DeletePaperSize(string printerName, string paperName)
        {
            if (!PaperExists(printerName, paperName))
            {
                return;
            }

            WinApi.structPrinterDefaults defaults = new WinApi.structPrinterDefaults();
            defaults.pDatatype = null;
            defaults.pDevMode = IntPtr.Zero;
            defaults.DesiredAccess = WinApi.PRINTER_ACCESS_ADMINISTER | WinApi.PRINTER_ACCESS_USE;

            IntPtr hPrinter = IntPtr.Zero;

            // Open the printer.
            if (WinApi.OpenPrinter(printerName, out hPrinter, ref defaults))
            {
                try
                {
                    // delete the form incase it already exists
                    WinApi.DeleteForm(hPrinter, paperName);
                }
                finally
                {
                    WinApi.ClosePrinter(hPrinter);
                }
            }
            else
            {
                StringBuilder strBuilder = new StringBuilder();
                strBuilder.AppendFormat("Failed to open the {0} printer, System error number: {1}",
                                        printerName, WinApi.GetLastError());
                throw new ApplicationException(strBuilder.ToString());
            }
        }

        public static bool PaperExists(string printerName, string paperName)
        {
            PrinterSettings prntSettings = new PrinterSettings();
            prntSettings.PrinterName = printerName;

            foreach (PaperSize ps in prntSettings.PaperSizes)
            {
                if (ps.PaperName == paperName)
                {
                    return true;
                }
            }

            return false;
        }
        #endregion
    }
    #endregion PrinterConfigurator
    
}

